/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 * Licensed under the Apache License, Version 2.0(the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* This file should never be included directly but, rather,
 * only indirectly through arch/chip/irq.h
 */

#ifndef __CHIPS_TC3XX_INCLUDE_UP_ATOMIC_H
#define __CHIPS_TC3XX_INCLUDE_UP_ATOMIC_H

#ifdef __cplusplus
extern "C" {
#endif

#include <nuttx/compiler.h>
#include <Platform_Types.h>

typedef volatile aligned_data(4) sint32 Atomic;

#if defined(__GNUC__) && defined(__TRICORE__)
#include "IfxCpu_IntrinsicsGcc.h"
/*  \brief          原子加
 *  \param[in/out]  ptr     需要进行原子加的数据地址
 *  \param[in]      addVal  要加的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicAdd(Atomic *ptr, sint32 addVal)
{
	volatile sint32 preVal;

	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal + addVal)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子减
 *  \param[in/out]  ptr     需要进行原子减的数据地址
 *  \param[in]      addVal  要减的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicSub(Atomic *ptr, sint32 subVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal - subVal)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子递增
 *  \param[in/out]  ptr     需要进行原子递增的数据地址
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicInc(Atomic *ptr)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal + 1U)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子递减
 *  \param[in/out]  ptr     需要进行原子减的数据地址
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicDec(Atomic *ptr)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal - 1U)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子或
 *  \param[in/out]  ptr     需要进行原子或的数据地址
 *  \param[in]      addVal  要或的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicOr(Atomic *ptr, sint32 orVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal | orVal)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子异或
 *  \param[in/out]  ptr     需要进行原子异或的数据地址
 *  \param[in]      addVal  要异或的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicXor(Atomic *ptr, sint32 xorVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal ^ xorVal)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子与
 *  \param[in/out]  ptr     需要进行原子与的数据地址
 *  \param[in]      addVal  要与的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicAnd(Atomic *ptr, sint32 andVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(preVal & andVal)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子与非
 *  \param[in/out]  ptr     需要进行原子与非的数据地址
 *  \param[in]      addVal  要与非的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicXand(Atomic *ptr, sint32 xandVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(~(preVal & xandVal))),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子设值
 *  \param[in/out]  ptr     需要进行原子设值的数据地址
 *  \param[in]      addVal  要设值的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicSet(Atomic *ptr, sint32 setVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(setVal)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子清值
 *  \param[in/out]  ptr     需要进行原子清值数据地址
 *  \param[in]      addVal  要清值的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicClean(Atomic *ptr)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				      ((unsigned int)(0x00U)),
				      ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子比较交换
*   \details        比较*ptr与oldval的，如果两者相等，则将newval更新到*ptr并返回true,否则返回false
 *  \param[in/out]  ptr     需要进行原子比较交换的数据地址
 *  \param[in]      oldVal  旧值
 *  \param[in]      newVal  新值
 *  \return
 *                  true(1)    比较交换成功
 *                  false(0)   比较交换失败
 */
static inline sint32 Os_AtomicCmpSwap(Atomic *ptr, sint32 oldVal, sint32 newVal)
{
	if (oldVal == __cmpAndSwap(((unsigned int *)(void *)(ptr)),
				   ((unsigned int)(newVal)),
				   ((unsigned int)(oldVal)))) {
		return 1;
	} else {
		return 0;
	}
}

#elif defined(__TASKING__)

/*  \brief          原子加
 *  \param[in/out]  ptr     需要进行原子加的数据地址
 *  \param[in]      addVal  要加的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicAdd(Atomic *ptr, sint32 addVal)
{
	register sint32 cmpVal;
	volatile sint32 preVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpswapw(((unsigned int *)(void *)(ptr)),
				    ((unsigned int)(preVal + addVal)),
				    ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子减
 *  \param[in/out]  ptr     需要进行原子减的数据地址
 *  \param[in]      addVal  要减的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicSub(Atomic *ptr, sint32 subVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpswapw(((unsigned int *)(void *)(ptr)),
				    ((unsigned int)(preVal - subVal)),
				    ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子递增
 *  \param[in/out]  ptr     需要进行原子递增的数据地址
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicInc(Atomic *ptr)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpswapw(((unsigned int *)(void *)(ptr)),
				    ((unsigned int)(preVal + 1U)),
				    ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子递减
 *  \param[in/out]  ptr     需要进行原子减的数据地址
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicDec(Atomic *ptr)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpswapw(((unsigned int *)(void *)(ptr)),
				    ((unsigned int)(preVal - 1U)),
				    ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子或
 *  \param[in/out]  ptr     需要进行原子或的数据地址
 *  \param[in]      addVal  要或的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicOr(Atomic *ptr, sint32 orVal)
{
	return __swapmskw(((unsigned int *)(ptr)), ((unsigned int)orVal),
			  ((unsigned int)orVal));
}

/*  \brief          原子异或
 *  \param[in/out]  ptr     需要进行原子异或的数据地址
 *  \param[in]      addVal  要异或的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicXor(Atomic *ptr, sint32 xorVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpswapw(((unsigned int *)(void *)(ptr)),
				    ((unsigned int)(preVal ^ xorVal)),
				    ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子与
 *  \param[in/out]  ptr     需要进行原子与的数据地址
 *  \param[in]      addVal  要与的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicAnd(Atomic *ptr, sint32 andVal)
{
	return __swapmskw(((unsigned int *)(ptr)), ((unsigned int)andVal),
			  ((unsigned int)~andVal));
}

/*  \brief          原子与非
 *  \param[in/out]  ptr     需要进行原子与非的数据地址
 *  \param[in]      addVal  要与非的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicXand(Atomic *ptr, sint32 xandVal)
{
	volatile sint32 preVal;
	register sint32 cmpVal;

	do {
		preVal = *ptr;
		cmpVal = __cmpswapw(((unsigned int *)(void *)(ptr)),
				    ((unsigned int)(~(preVal & xandVal))),
				    ((unsigned int)(preVal)));
	} while (cmpVal != preVal);
	return preVal;
}

/*  \brief          原子设值
 *  \param[in/out]  ptr     需要进行原子设值的数据地址
 *  \param[in]      addVal  要设值的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicSet(Atomic *ptr, sint32 setVal)
{
	return __swapmskw(((unsigned int *)(ptr)), ((unsigned int)setVal),
			  ((unsigned int)0xFFFFFFFFUL));
}

/*  \brief          原子清值
 *  \param[in/out]  ptr     需要进行原子清值数据地址
 *  \param[in]      addVal  要清值的数值
 *  \return         原子操作前的值
 */

static inline Atomic Os_AtomicClean(Atomic *ptr)
{
	return __swapmskw(((unsigned int *)(ptr)), ((unsigned int)0x00U),
			  ((unsigned int)0xFFFFFFFFUL));
}

/*  \brief          原子比较交换
 *   \details
 * 比较*ptr与oldval的，如果两者相等，则将newval更新到*ptr并返回true,
 * 否则返回false
 *  \param[in/out]  ptr     需要进行原子比较交换的数据地址
 *  \param[in]      oldVal  旧值
 *  \param[in]      newVal  新值
 *  \return
 *                  true(1)    比较交换成功
 *                  false(0)   比较交换失败
 */
static inline sint32 Os_AtomicCmpSwap(Atomic *ptr, sint32 oldVal, sint32 newVal)
{
	if (oldVal == __cmpswapw(((unsigned int *)(void *)(ptr)),
				 ((unsigned int)(newVal)),
				 ((unsigned int)(oldVal)))) {
		return 1;
	} else {
		return 0;
	}
}
#endif /*__TASKING__*/
#ifdef __cplusplus
}
#endif

#endif /* __CHIPS_TC3XX_INCLUDE_UP_ATOMIC_H */
